8-4 高速缓存方案cache-manager
1. 缓存方案概述
1.1 NestJS官方集成
NestJS官方提供的@nestjs/cache-manager
模块是对cache-manager库的深度封装,为开发者提供了开箱即用的缓存解决方案。其核心优势体现在:
- 依赖注入系统:
- 通过
@Inject(CACHE_MANAGER)
装饰器实现缓存实例的自动注入 - 支持模块化配置,可在不同模块中注册独立的缓存实例
- 示例代码:
@Module({ imports: [CacheModule.register()], providers: [MyService] }) export class AppModule {}
typescript
- 通过
- 装饰器支持:
装饰器 功能说明 使用场景示例 @CacheKey()
自定义缓存键名 区分相同接口的不同参数查询 @CacheTTL()
设置单个接口的缓存过期时间 针对特殊接口调整缓存策略 @Cache()
组合式装饰器(NestJS 8+新增) 简化缓存配置 - 统一接口:
- 提供
get()
/set()
/del()
等标准方法 - 底层存储引擎切换不影响业务代码
- 支持Promise和Observable两种异步模式
- 提供
💡 最佳实践:在大型项目中,建议创建自定义的CacheService
封装类,统一处理缓存异常和日志记录。
1.2 多存储引擎支持
cache-manager的插件化架构使其支持多种存储后端,各引擎特性对比如下:
扩展知识:
- 混合缓存策略:可以组合多个存储引擎,例如:
CacheModule.register({ store: multiCaching([ memoryStore, // 一级缓存(内存) redisStore // 二级缓存(Redis) ]) })
typescript - 自定义存储引擎:通过实现
Store
接口可接入任意存储系统 - 性能指标(基于基准测试):
- 内存操作:~50,000 ops/sec
- Redis远程:~15,000 ops/sec
- 文件存储:~2,000 ops/sec
常见问题:
Q: 如何选择存储引擎?
A: 根据数据特性决定: - 临时数据 → 内存存储 - 重要业务数据 → Redis - 历史归档数据 → 文件系统/数据库延伸学习:
最新版本(v5.2.0)新增了对WebSocket实时缓存更新的支持,可通过
cacheManager.watch()
方法监听数据变更。
2. 支持的存储引擎
2.1 内存存储(默认)
内存存储是cache-manager的默认引擎,特别适合快速开发和测试环境:
深入特性:
- 极速响应:纳秒级读写速度(基准测试显示~50,000 ops/sec)
- 智能垃圾回收:通过WeakMap实现自动内存管理
- 多实例隔离:不同CacheModule实例拥有独立内存空间
进阶配置:
CacheModule.register({
store: 'memory',
options: {
max: 1000, // 最大缓存条目数
ttl: 60 * 1000, // 默认过期时间(ms)
clone: true, // 深拷贝存储值
dispose: (key, value) => {} // 条目被淘汰时的回调
}
})
typescript
典型应用场景:
- 应用启动时加载的配置信息
- JWT令牌的短期缓存
- 高频调用的计算中间结果
⚠️ 生产环境注意事项:
- 需要监控内存使用量(建议使用
process.memoryUsage()
) - 避免缓存大型对象(超过1MB)
- 配合
max
参数防止内存泄漏
2.2 Redis引擎
Redis引擎是企业级应用的首选方案:
深度集成特性:
- 集群支持:自动识别Redis Cluster模式
- 管道优化:批量操作减少网络往返
- TLS加密:支持SSL/TLS安全连接
- 哨兵模式:自动故障转移保障高可用
专业配置示例:
import * as redisStore from 'cache-manager-ioredis';
CacheModule.register({
store: redisStore,
host: 'redis-cluster.example.com',
port: 6379,
password: 'securepassword',
db: 1,
ttl: 86400,
enableOfflineQueue: true, // 断线自动重试
connectTimeout: 5000 // 连接超时设置
});
typescript
性能优化技巧:
- 使用
scan
替代keys
命令避免阻塞 - 合理设置
maxmemory-policy
淘汰策略 - 启用
ioRedis.defineCommand
添加自定义Lua脚本
🔍 版本兼容性矩阵:
cache-manager版本 | Redis协议 | 关键特性 |
---|---|---|
v4.x | <=5.0 | 基础功能 |
v5.0 | 6.0 | ACL支持 |
v5.1 | 6.2 | RESP3协议 |
2.3 LRU缓存策略
LRU(Least Recently Used)策略是内存优化的利器:
高级配置参数:
CacheModule.register({
store: require('cache-manager-lru'),
options: {
max: 500, // 最大条目数
length: (n) => n.length, // 自定义计算条目大小
maxAge: 1000 * 60 * 60 // 全局过期时间(ms)
}
})
typescript
算法变体对比:
算法类型 | 时间复杂度 | 特点 |
---|---|---|
标准LRU | O(1) | 双向链表+哈希表 |
LRU-K | O(k) | 考虑历史访问频率 |
2Q | O(1) | 双队列优化 |
实战技巧:
- 对缓存命中率进行监控:
const hits = cache.stats.hits; const misses = cache.stats.misses; const ratio = hits / (hits + misses);
javascript - 配合
maxAge
实现TTL+LRU双重淘汰
2.4 其他存储引擎
文件系统存储
专业配置:
CacheModule.register({
store: require('cache-manager-fs'),
options: {
path: '/var/cache', // 存储路径
ttl: 3600, // 秒为单位
maxsize: 1000 * 1000 * 10, // 10MB限制
zip: true // 启用压缩
}
})
typescript
数据库存储引擎对比
引擎 | 读写性能 | 事务支持 | 适用数据量 |
---|---|---|---|
MongoDB | 高 | ❌ | 大数据量 |
PostgreSQL | 中 | ✅ | 结构化数据 |
SQLite | 低 | ✅ | 小型应用 |
混合存储架构示例:
💡 专家建议:
- 对冷数据采用分层存储策略
- 使用
redis-memcached
协议转换器兼容旧系统 - 定期执行
cache.compact()
优化存储空间
延伸阅读:
3. 基础使用(内存缓存)
3.1 模块注册与配置
内存缓存的配置提供了丰富的选项来满足不同场景需求:
// 高级配置示例
@Module({
imports: [
CacheModule.register({
ttl: 3000, // 默认缓存时间(毫秒)
max: 100, // 最大缓存条目数
isCacheable: (val) => { // 自定义缓存验证
return val !== undefined && val !== null;
},
store: 'memory', // 显式指定存储引擎
clone: true // 深拷贝存储值
})
]
})
typescript
版本差异处理建议:
// 版本兼容性处理
const cacheConfig = {
ttl: process.env.CACHE_VERSION === 'v4' ? 3 : 3000 // 自动转换秒/毫秒
};
typescript
动态配置技巧:
- 使用
ConfigService
读取环境变量 - 根据
NODE_ENV
切换开发/生产配置 - 支持热更新配置(通过
CacheModule.forRootAsync
)
3.2 缓存实例注入
注入的缓存实例支持多种高级用法:
// 类型安全的注入方式
import { Cache } from 'cache-manager';
constructor(
@Inject(CACHE_MANAGER)
private cacheManager: Cache & {
store: {
getClient?: () => RedisClient; // 类型扩展
keys?: (pattern: string) => Promise<string[]>;
}
}
) {}
typescript
多缓存实例场景:
// 注册命名缓存实例
CacheModule.register({
name: 'userCache',
ttl: 5000
})
// 注入指定实例
@Inject('userCache') private userCache: Cache
typescript
3.3 核心操作方法
扩展后的方法表格包含更多实用信息:
方法 | 参数说明 | 返回值 | 时间复杂度 | 使用建议 |
---|---|---|---|---|
set() | key: string, value: any, ttl?: number | Promise<void> | O(1) | 为重要数据单独设置TTL |
get() | key: string | Promise<any> | O(1) | 配合try-catch处理异常 |
del() | key: string | Promise<void> | O(1) | 批量删除使用模式匹配 |
reset() | - | Promise<void> | O(n) | 谨慎使用,影响所有缓存数据 |
wrap() | key: string, fn: () => Promise<any> | Promise<any> | O(1) | 实现"缓存穿透"保护 |
高级方法示例:
// 原子性操作示例
const result = await cacheManager.wrap('user:1', async () => {
return await this.userService.findById(1);
});
// 批量操作模式
const keys = await cacheManager.store.keys('user:*');
await Promise.all(keys.map(k => cacheManager.del(k)));
typescript
3.4 内存缓存限制及解决方案
深度分析限制与应对策略:
限制类型 | 具体表现 | 解决方案 | 实施示例 |
---|---|---|---|
数据易失性 | 进程重启数据丢失 | 1. 定期持久化到文件 2. 启动时热加载 | 使用cache-manager-fs 做二级缓存 |
内存压力 | 大对象导致OOM | 1. 限制条目大小 2. 启用压缩 | options: { maxSize: 1024 } |
分布式不一致 | 多实例缓存不同步 | 1. Redis广播机制 2. 消息队列通知 | 集成socket.io-redis |
GC性能问题 | 频繁GC影响性能 | 1. 避免缓存DOM对象 2. 控制缓存生命周期 | 设置合理的TTL |
生产环境建议:
- 实施缓存监控:
setInterval(() => { const usage = process.memoryUsage(); monitor.record('cache_memory', usage.heapUsed); }, 5000);
typescript - 建立缓存治理策略:
- 分级缓存(L1/L2)
- 熔断机制(当缓存故障时自动降级)
- 压测确定最优配置参数
性能优化技巧:
// 使用Object.freeze避免意外修改
await cacheManager.set('config', Object.freeze(config));
// 采用Buffer存储二进制数据
await cacheManager.set('image', Buffer.from(imageData));
typescript
扩展阅读:
4. 接口级缓存
4.1 自动缓存拦截器(增强版)
import { CacheInterceptor, CacheTTL } from '@nestjs/cache-manager';
@Controller()
@UseInterceptors(CacheInterceptor)
export class AppController {
// 动态TTL设置示例
@Get('dynamic-data')
@CacheTTL((req) => req.query.ttl || 5000) // 支持请求参数动态设置
async getDynamicData() {
return this.dataService.getComplexData();
}
// 缓存键定制示例
@Get('user/:id')
@CacheKey((req) => `user_${req.params.id}_v2`) // 自定义缓存键生成规则
async getUser(@Param('id') id: string) {
return this.userService.findById(id);
}
}
ts
高级特性:
- 条件缓存:
@UseInterceptors(new CacheInterceptor({ ignoreCache: (req) => req.headers['cache-control'] === 'no-cache' }))
typescript - 响应转换:
@UseInterceptors(new CacheInterceptor({ transform: (response) => _.pick(response, ['id', 'name']) }))
typescript - 多级缓存:
4.2 缓存更新策略(专业版)
4.2.1 TTL过期(增强实现)
// 动态TTL策略
@CacheTTL((req, res) => {
const data = res.data;
return data.volatile ? 1000 : 60000; // 根据数据特性设置
})
@Get('smart-data')
getSmartData() { ... }
typescript
TTL优化建议:
- 热数据:短TTL(1-5秒)
- 温数据:中等TTL(1-5分钟)
- 冷数据:长TTL(1小时+)
4.2.2 手动更新(企业级方案)
@Get('products')
async getProducts(@Query() params) {
const cacheKey = `products_${JSON.stringify(params)}`;
if (params.refresh) {
await this.cacheManager.del(cacheKey);
}
return this.cacheManager.wrap(cacheKey, () => {
return this.productService.findAll(params);
}, { ttl: 60000 });
}
typescript
高级模式:
- 批量刷新:
@Post('products/refresh') async refreshProducts(@Body() ids: string[]) { await Promise.all( ids.map(id => this.cacheManager.del(`product_${id}`) ) ); }
typescript - 版本控制:
const CACHE_VERSION = 'v3'; const cacheKey = `data_${CACHE_VERSION}_${id}`;
typescript
4.2.3 数据库钩子(生产级实现)
@Entity()
export class Product {
@AfterUpdate()
async updateCache() {
const keys = await this.cacheManager.store.keys(`product_${this.id}_*`);
await Promise.all(keys.map(k => this.cacheManager.del(k)));
// 关联缓存更新
await this.cacheManager.del('featured_products');
}
@AfterRemove()
async cleanCache() {
await this.cacheManager.del(`product_${this.id}`);
}
}
typescript
钩子优化:
- 异步队列处理
- 批量操作减少IO
- 失败重试机制
4.3 最佳实践场景(专家建议)
推荐缓存场景(扩展):
- 地理空间数据:GIS计算结果缓存
- 机器学习模型:推理结果缓存
- API聚合数据:组合多个微服务的结果
// API聚合缓存示例
@Get('dashboard')
@CacheTTL(30000)
async getDashboard() {
const [users, products, stats] = await Promise.all([
this.userService.getCount(),
this.productService.getFeatured(),
this.analyticsService.getStats()
]);
return { users, products, stats };
}
typescript
规避缓存场景(深入分析):
- 金融交易数据:
- 实时余额查询
- 股票最新价格
- 权限敏感数据:
- 用户个人隐私信息
- 角色权限变更
- 实时通讯:
- 在线聊天消息
- 游戏状态同步
缓存雪崩防护方案:
// 随机TTL避免同时过期
const ttl = 5000 + Math.floor(Math.random() * 2000); // 5-7秒随机区间
typescript
缓存穿透防护:
async getProduct(id: string) {
const cacheKey = `product_${id}`;
const cached = await this.cacheManager.get(cacheKey);
if (cached === null) { // 明确缓存空值
return null;
}
if (!cached) {
const data = await this.productService.findById(id);
await this.cacheManager.set(cacheKey, data || null, 30000);
return data;
}
return cached;
}
typescript
行业案例参考:
- 电商平台:
- 商品详情页:5分钟TTL
- 库存信息:实时查询+本地缓存3秒
- 社交网络:
- 用户资料:1小时TTL
- 好友列表:15分钟TTL+变更推送
性能指标参考:
场景 | 缓存前延迟 | 缓存后延迟 | QPS提升 |
---|---|---|---|
商品详情查询 | 120ms | 15ms | 8x |
用户推荐列表 | 250ms | 30ms | 6x |
地理位置计算 | 800ms | 50ms | 16x |
扩展工具推荐:
- Redis Insight - 可视化缓存监控
- Cachegoose - Mongoose缓存插件
- NestJS Caching Module - 官方增强模块
5. 全局缓存配置(生产级实践)
5.1 全局拦截器启用(增强实现)
// main.ts
app.useGlobalInterceptors(
new CacheInterceptor({
// 高级配置选项
trackBy: (context) => {
const request = context.switchToHttp().getRequest();
return `${request.method}_${request.url}`; // 自定义缓存键生成规则
},
exclude: [
'/auth/*', // 排除认证接口
'/admin/*', // 排除管理接口
{ path: '/metrics', method: RequestMethod.GET } // 排除特定路由
],
maxAge: 10000, // 全局默认缓存时间
store: 'memory' // 指定存储引擎
})
);
typescript
关键配置项说明:
trackBy
:支持基于请求特征生成缓存键exclude
:支持多种排除模式(通配符/精确路径/方法组合)maxAge
:全局默认TTL(会被方法级装饰器覆盖)store
:可动态切换存储引擎(需提前注册)
性能优化技巧:
// 配合路由元数据动态控制
const reflector = new Reflector();
app.useGlobalInterceptors(
new CacheInterceptor({
ignoreCache: (context) => {
return reflector.get<boolean>('noCache', context.getHandler());
}
})
);
// 在控制器中使用
@Get('live-data')
@SetMetadata('noCache', true)
getLiveData() { ... }
typescript
5.2 自定义装饰器(企业级方案)
5.2.1 @CacheKey(增强版)
// 高级缓存键装饰器
export function DynamicCacheKey(fn: (req: Request) => string) {
return applyDecorators(
SetMetadata('cacheKeyBuilder', fn),
UseInterceptors(CacheKeyInterceptor)
);
}
// 使用示例
@DynamicCacheKey((req) => `report_${req.query.type}_v2`)
@Get('reports')
getReports(@Query() query) { ... }
// 配套拦截器实现
class CacheKeyInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
const request = context.switchToHttp().getRequest();
const keyBuilder = this.getCacheKeyBuilder(context);
if (keyBuilder) {
request.cacheKey = keyBuilder(request);
}
return next.handle();
}
}
typescript
应用场景:
- 多租户系统的租户隔离缓存
- 多语言站点的内容版本控制
- A/B测试的不同变体缓存
5.2.2 @CacheTTL(动态控制)
// 支持动态TTL计算的装饰器工厂
export function SmartTTL(fn: (res: any) => number) {
return applyDecorators(
SetMetadata('ttlCalculator', fn),
UseInterceptors(TTLInterceptor)
);
}
// 使用示例
@SmartTTL((res) => res.data.volatile ? 1000 : 60000)
@Get('smart-data')
getSmartData() { ... }
// 响应数据示例
// { data: { volatile: true, ... }, ... }
typescript
动态TTL策略:
- 数据敏感度分级:
@SmartTTL((res) => { switch(res.data.securityLevel) { case 'high': return 1000; case 'medium': return 10000; default: return 60000; } })
typescript - 流量自适应:
@SmartTTL(() => { const qps = getCurrentQPS(); return qps > 1000 ? 3000 : 10000; })
typescript
复合装饰器示例:
// 组合式缓存控制
export function CacheControl(options: {
key?: string | ((req: Request) => string);
ttl?: number | ((res: any) => number);
}) {
return applyDecorators(
options.key ?
(typeof options.key === 'string' ?
@CacheKey(options.key) :
@DynamicCacheKey(options.key)) : () => {},
options.ttl ?
(typeof options.ttl === 'number' ?
@CacheTTL(options.ttl) :
@SmartTTL(options.ttl)) : () => {}
);
}
// 使用示例
@CacheControl({
key: (req) => `user_${req.params.id}`,
ttl: (res) => res.data.premium ? 3600000 : 60000
})
@Get('users/:id')
getUser(@Param('id') id: string) { ... }
typescript
5.3 全局缓存监控(生产必备)
// 缓存统计中间件
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const cacheStatus = res.getHeader('X-Cache-Status');
monitor.record('cache', {
path: req.path,
status: cacheStatus || 'miss',
duration: Date.now() - start
});
});
next();
});
// 配合拦截器设置响应头
class CacheInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
const cached = checkCache();
if (cached) {
context.switchToHttp().getResponse()
.setHeader('X-Cache-Status', 'hit');
}
return cached ? of(cached) : next.handle();
}
}
typescript
监控指标建议:
- 缓存命中率(Hit/Miss Ratio)
- 缓存响应时间分布
- 存储引擎内存使用量
- TTL过期分布统计
告警规则示例:
- 当命中率 < 60% 持续5分钟
- 当平均缓存延迟 > 50ms
- 当内存使用 > 80% 阈值
5.4 安全加固方案
// 缓存注入防护
CacheModule.register({
store: redisStore,
injectionDefense: {
enabled: true,
maxKeyLength: 256, // 防止超长键攻击
prohibitedPatterns: [ // 禁止危险模式
/[\$\{\}]/,
/\.\.\//
]
}
});
// 敏感数据过滤
class SecureCacheInterceptor extends CacheInterceptor {
async intercept(context: ExecutionContext) {
const response = await super.intercept(context);
return removeSensitiveFields(response);
}
}
typescript
安全最佳实践:
- 对缓存键进行SHA256哈希处理
- 实现缓存数据加密(如AES-GCM)
- 定期轮换加密密钥
- 禁用DEBUG命令(针对Redis)
5.5 多环境配置策略
// config/cache.config.ts
export default registerAs('cache', () => ({
defaultTTL: process.env.NODE_ENV === 'production' ? 60000 : 3000,
store: process.env.CACHE_STORE || 'memory',
redis: {
host: process.env.REDIS_HOST,
tls: process.env.NODE_ENV === 'production'
}
}));
// 模块动态注册
CacheModule.registerAsync({
useFactory: (config: ConfigService) => config.get('cache'),
inject: [ConfigService]
});
typescript
环境差异建议:
环境 | TTL策略 | 存储引擎 | 安全要求 |
---|---|---|---|
开发环境 | 短TTL(1-5秒) | 内存 | 基础验证 |
测试环境 | 中等TTL(1分钟) | Redis | 完整验证 |
生产环境 | 分级TTL | Redis集群 | 全加密+监控 |
扩展工具推荐:
- NestJS Config - 环境配置管理
- Keyv - 多存储统一接口
- CacheManager - 核心库文档
6. Redis集成实践(生产级配置)
6.1 依赖安装与版本控制
# 推荐安装特定版本(保持环境一致性)
npm install cache-manager-ioredis@3.0.0
# 配套安装ioredis(最新稳定版)
npm install ioredis@5.3.2
bash
版本兼容性矩阵:
cache-manager-ioredis | ioredis | Node.js | 关键特性 |
---|---|---|---|
v2.x | v4.x | >=12 | 基础功能 |
v3.x | v5.x | >=14 | 集群优化 |
v4.x (beta) | v6.x | >=16 | RESP3协议 |
6.2 生产级模块配置
import { redisStore } from 'cache-manager-ioredis';
import { Cluster } from 'ioredis';
// 集群模式配置
const redisCluster = new Cluster([
{ host: 'redis-node-1', port: 6379 },
{ host: 'redis-node-2', port: 6380 }
]);
CacheModule.registerAsync({
useFactory: () => ({
store: redisStore,
redisInstance: redisCluster, // 复用现有连接
ttl: 30000,
max: 1000, // 最大连接数
connectTimeout: 5000, // 连接超时
commandTimeout: 3000, // 操作超时
enableAutoPipelining: true, // 启用管道
enableOfflineQueue: false // 生产环境建议关闭
}),
inject: [ConfigService]
});
typescript
高级配置项说明:
redisInstance
:复用现有连接池enableAutoPipelining
:提升批量操作性能(提升30%-50%)enableOfflineQueue
:生产环境建议禁用避免内存堆积
TLS加密配置示例:
store: redisStore({
tls: {
ca: fs.readFileSync('redis_ca.pem'),
cert: fs.readFileSync('redis_cert.pem'),
key: fs.readFileSync('redis_key.pem')
}
})
typescript
6.3 Redis优势深度分析
1. 数据持久化机制
策略选择建议:
- RDB:适合大规模数据恢复(性能高)
- AOF:适合数据安全优先(可配置fsync策略)
- 混合模式:生产环境推荐(RDB+AOF)
2. 高可用架构实现
// 哨兵模式配置
const sentinel = new Redis({
sentinels: [
{ host: 'sentinel-1', port: 26379 },
{ host: 'sentinel-2', port: 26380 }
],
name: 'mymaster'
});
CacheModule.register({
store: redisStore,
redisInstance: sentinel
});
typescript
故障转移过程:
- 哨兵检测主节点下线
- 自动选举新主节点
- 应用透明重连(需配置
autoReconnect: true
)
3. 分布式缓存实践
多级缓存架构:
数据分片策略:
- 哈希分片:
CRC16(key) % 16384
- 标签分片:
{user1000}.profile
4. 性能优化指标
操作类型 | 内存缓存延迟 | Redis本地延迟 | Redis远程延迟 |
---|---|---|---|
GET操作 | 0.01ms | 0.1ms | 1-2ms |
SET操作 | 0.02ms | 0.2ms | 1-3ms |
批量GET(100条) | 0.5ms | 5ms | 50-100ms |
优化建议:
- 使用
multi()
命令批量操作 - 合理设置
maxmemory-policy
(推荐volatile-lru) - 热点数据配合本地缓存
6.4 监控与维护
关键监控指标:
# 通过redis-cli获取
> INFO memory # 内存使用情况
> INFO stats # 操作统计
> INFO replication # 复制状态
bash
告警规则示例:
- 内存使用率 > 80%
- 连接数 > maxclients的90%
- 持久化失败持续5分钟
维护命令参考:
# 手动触发RDB持久化
redis-cli BGSAVE
# 清理过期键
redis-cli SCAN 0 MATCH * COUNT 1000 | xargs redis-cli DEL
bash
6.5 常见问题解决方案
问题1:缓存穿透
// 空值缓存方案
async getProduct(id: string) {
const key = `product:${id}`;
const cached = await cacheManager.get(key);
if (cached === null) return null; // 已缓存空值
if (!cached) {
const data = await db.getProduct(id);
await cacheManager.set(key, data || null, 30000);
return data;
}
return cached;
}
typescript
问题2:缓存雪崩
// 随机TTL分散过期
CacheModule.register({
store: redisStore,
ttl: () => 30000 + Math.floor(Math.random() * 10000) // 30-40秒随机区间
});
typescript
问题3:大Key优化
// 分片存储大对象
async setLargeData(key: string, data: any) {
const chunks = _.chunk(data, 100);
await Promise.all(
chunks.map((chunk, i) =>
cacheManager.set(`${key}:${i}`, chunk)
);
}
typescript
6.6 扩展阅读
↑